/**
 * 
 */
package cn.springsoter.core.tool.utils;

import java.security.KeyFactory;
import java.security.KeyPair;
import java.security.KeyPairGenerator;
import java.security.NoSuchAlgorithmException;
import java.security.PrivateKey;
import java.security.PublicKey;
import java.security.Security;
import java.security.Signature;
import java.security.spec.PKCS8EncodedKeySpec;
import java.security.spec.X509EncodedKeySpec;
import java.util.Base64;
import java.util.Objects;

import org.apache.commons.lang3.StringUtils;
import org.bouncycastle.jce.provider.BouncyCastleProvider;

import com.google.common.io.BaseEncoding;

import lombok.extern.slf4j.Slf4j;

/**
 * 非对称加密算法：Ed25519 工具类
 * 
 * @author kevin
 *
 */
@Slf4j
public class Ed25519Util {
	//算法名称：Ed25519
	static final String ALGORITHM = "Ed25519";
	//初始化算法提供者：BouncyCastleProvider
	static final BouncyCastleProvider BOUNCY_CASTLE_PROVIDER = new BouncyCastleProvider();
	
	static {
		//注册算法提供者，启动时注册一次即可
		Security.addProvider(BOUNCY_CASTLE_PROVIDER);
	}
	
	/**
	 * 生成公私钥对
	 * 
	 * @return
	 */
	public static KeyPair generateKeyPair() {
		try {
			return KeyPairGenerator.getInstance(ALGORITHM).generateKeyPair();
		} catch (NoSuchAlgorithmException e) {
			log.error("=====NoSuchAlgorithmException====", e);
		}
		return null;
	}
	
	/**
	 * 获取公钥对象
	 * 
	 * @return
	 */
	private static PublicKey getPublicKey() {
		KeyPair keyPair = generateKeyPair();
		if(Objects.nonNull(keyPair)) {
			return keyPair.getPublic();
		}
		return null;
	}
	
	/**
	 * 获取私钥对象
	 * 
	 * @return
	 */
	private static PrivateKey getPrivateKey() {
		KeyPair keyPair = generateKeyPair();
		if(Objects.nonNull(keyPair)) {
			return keyPair.getPrivate();
		}
		return null;
	}
	
	/**
	 * 获取公钥的base64加密字符串
	 * 
	 * @return
	 */
	public static String getBase64PublicKey() {
		PublicKey publicKey = getPublicKey();
		if(Objects.nonNull(publicKey)) {
			return Base64.getEncoder().encodeToString(publicKey.getEncoded());
		}
		return null;
	}
	
	/**
	 * 获取私钥的base64加密字符串
	 * 
	 * @return
	 */
	public static String getBase64PrivateKey() {
		PrivateKey privateKey = getPrivateKey();
		if(Objects.nonNull(privateKey)) {
			return Base64.getEncoder().encodeToString(privateKey.getEncoded());
		}
		return null;
	}
	
	/**
	 * 生成签名（二进制）
	 * 
	 * @param privateKey
	 * @param text
	 * @return
	 */
	public static byte[] genSignature(PrivateKey privateKey, String text) {
		if(Objects.isNull(privateKey) || StringUtils.isBlank(text)) {
			log.error("Parameter: privateKey/text can not be null!");
			return null;
		}
		Signature signature = null;
		try {
			signature = Signature.getInstance(ALGORITHM, BOUNCY_CASTLE_PROVIDER);
			signature.initSign(privateKey);
			signature.update(text.getBytes());
			return signature.sign();
		} catch (Exception e) {
			log.error("=====SignException====", e);
		}
		return null;
	}
	
	/**
	 * 生成签名（二进制）
	 * 
	 * @param base64PrivateKey		base64加密的私钥字符串
	 * @param text					文本内容
	 * @return
	 */
	public static byte[] genSignature(String base64PrivateKey, String text) {
		if(StringUtils.isBlank(base64PrivateKey) || StringUtils.isBlank(text)) {
			log.error("Parameter: base64PrivateKey/text can not be null!");
			return null;
		}
		try {
			byte[] message = text.getBytes("utf-8");
			
			byte[] privateKeyBytes = Base64.getDecoder().decode(base64PrivateKey);
			KeyFactory keyFactory = KeyFactory.getInstance(ALGORITHM);
			PKCS8EncodedKeySpec pkcs8KeySpec = new PKCS8EncodedKeySpec(privateKeyBytes);
			PrivateKey privateKey = keyFactory.generatePrivate(pkcs8KeySpec);
			
			//验证签名
			Signature signature = Signature.getInstance(ALGORITHM, BOUNCY_CASTLE_PROVIDER);
			signature.initSign(privateKey);
			signature.update(message);
			return signature.sign();
		} catch (Exception e) {
		}
		return null;
	}
	
	/**
	 * 生成base64加密签名字符串
	 * 
	 * @param base64PrivateKey		base64加密的私钥字符串
	 * @param text					文本内容
	 * @return
	 */
	public static String genBase64Signature(String base64PrivateKey, String text) {
		byte[] signature = genSignature(base64PrivateKey, text);
		if(Objects.nonNull(signature)) {
			return Base64.getEncoder().encodeToString(signature);
		}
		return null;
	}
	
	/**
	 * 生成base64加密签名字符串
	 * 
	 * @param privateKey	私钥对象
	 * @param text			文本内容
	 * @return
	 */
	public static String genBase64Signature(PrivateKey privateKey, String text) {
		byte[] signature = genSignature(privateKey, text);
		if(Objects.nonNull(signature)) {
			return Base64.getEncoder().encodeToString(signature);
		}
		return null;
	}
	
	/**
	 * 验证签名
	 * 
	 * @param base64PublicKey		base64加密公钥字符串
	 * @param signature				需要验证的base64签名
	 * @param text					文本内容
	 * @return
	 */
	public static boolean verify(String base64PublicKey, String signature, String text) {
		if(StringUtils.isBlank(base64PublicKey) || StringUtils.isBlank(signature) || StringUtils.isBlank(text)) {
			log.error("Parameter: publicKey/signature/text can not be null!");
			return false;
		}
		try {
			byte[] message = text.getBytes("utf-8");
			byte[] sig = BaseEncoding.base64().decode(signature);
			
			byte[] publicKeyBytes = Base64.getDecoder().decode(base64PublicKey);
			KeyFactory keyFactory = KeyFactory.getInstance(ALGORITHM);
			X509EncodedKeySpec x509EncodedKeySpec = new X509EncodedKeySpec(publicKeyBytes);
			PublicKey pk = keyFactory.generatePublic(x509EncodedKeySpec);
			
			//验证签名
			Signature verify = Signature.getInstance(ALGORITHM, BOUNCY_CASTLE_PROVIDER);
			verify.initVerify(pk);
			verify.update(message);
			return verify.verify(sig);
		} catch (Exception e) {
			log.error("=====VerifyException====", e);
		}
    	return false;
	}
	
	/**
	 * 验证签名
	 * 
	 * @param publicKey				公钥对象
	 * @param signature				需要验证的base64签名
	 * @param text					文本内容
	 * @return
	 */
	public static boolean verify(PublicKey publicKey, String signature, String text) {
		if(Objects.isNull(publicKey) || StringUtils.isBlank(signature) || StringUtils.isBlank(text)) {
			log.error("Parameter: publicKey/signature/text can not be null!");
			return false;
		}
		try {
			byte[] message = text.getBytes("utf-8");
			byte[] sig = BaseEncoding.base64().decode(signature);
			
			//验证签名
			Signature verify = Signature.getInstance(ALGORITHM, BOUNCY_CASTLE_PROVIDER);
			verify.initVerify(publicKey);
			verify.update(message);
			return verify.verify(sig);
		} catch (Exception e) {
			log.error("=====VerifyException====", e);
		}
		return false;
	}
}
